﻿using System;
using System.Diagnostics;

using zijian666.Converts.Abstractions;

namespace zijian666.Converts.Core;

/// <summary>
/// 转换结果
/// </summary>
[DebuggerDisplay("{Success} : {Success ? (object)Value : Exception.Message}")]
public readonly struct ConvertResult<T> : IObjectProxy
{
    /// <summary>
    /// 表示一个转换失败但没有异常的返回值
    /// </summary>
    public static readonly ConvertResult<T> FAIL = new(false, default, null);

    /// <summary>
    /// 转换成功
    /// </summary>
    /// <param name="value">返回值</param>
    public ConvertResult(T? value) : this(true, value, null) { }

    /// <summary>
    /// 构造一个转换结果
    /// </summary>
    /// <param name="success">是否成功</param>
    /// <param name="value">返回值</param>
    /// <param name="ex">错误对象</param>
    private ConvertResult(bool success, T? value, Exception? ex)
    {
        Success = success;
        _value = value;
        Exception = ex;
    }

    /// <summary>
    /// 转换结果值
    /// </summary>
    private readonly T? _value;

    /// <summary>
    /// 是否成功
    /// </summary>
    /// <returns></returns>
    public bool Success { get; }

    /// <summary>
    /// 转换后的输出结果
    /// </summary>
    /// <returns></returns>
    public T? Value
    {
        get
        {
            if (Exception is not null)
            {
                throw Exception;
            }
            return _value;
        }
    }

    /// <summary>
    /// 获取返回值或默认值
    /// </summary>
    /// <param name="defaultValue">默认值</param>
    public T? GetValueOrDefalut(T? defaultValue) => Success ? _value : defaultValue;

    /// <summary>
    /// 如果失败,则返回异常
    /// </summary>
    /// <returns></returns>
    public Exception? Exception { get; }

    /// <inheritdoc />
    object? IObjectProxy.GetActualObject() => Value;

    /// <inheritdoc />
    public override bool Equals(object obj) => obj is ConvertResult<T> result && Equals(result.Value, Value);

    /// <inheritdoc />
    public override int GetHashCode() => Value?.GetHashCode() ?? 0;

    /// <inheritdoc />
    public static bool operator ==(ConvertResult<T> left, ConvertResult<T> right) => left.Equals(right);

    /// <inheritdoc />
    public static bool operator !=(ConvertResult<T> left, ConvertResult<T> right) => !(left == right);

    #region 隐式转换

    /// <inheritdoc />
    public static implicit operator ConvertResult<T>(Exception exception)
    {
        if (exception is null)
        {
            throw new ArgumentNullException(nameof(exception));
        }

        return new ConvertResult<T>(false, default, exception);
    }

    /// <inheritdoc />
    public static implicit operator ConvertResult<T>(T? value) => new(value);

    #endregion
}
